package com.fishery.search.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fishery.search.config.MyElasticSearchProperties;
import com.fishery.search.entity.SearchCondition;
import com.fishery.search.service.DiagnoseSearch;
import com.fishery.search.util.CamelUtil;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Component
public class DiagnoseSearchImpl implements DiagnoseSearch {

	@Autowired
	@Qualifier("restHighLevelClient")
	private RestHighLevelClient client;

	@Autowired
	private MyElasticSearchProperties properties;


	/**
	 * 构建查询表达式
	 *
	 * @param searchCondition
	 * @param sortOrderMap
	 * @return
	 */
	private SearchSourceBuilder buildSearchSource(SearchCondition searchCondition, Map<String, SortOrder> sortOrderMap) {
		// 构建文档查询表达式
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		// 构建查询条件
		MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery(searchCondition.getKeyword(), searchCondition.getContent());
		// 设置查询的页码、尺寸
		searchSourceBuilder.from((searchCondition.getCurrent() - 1) * searchCondition.getSize())
				.size(searchCondition.getSize()).timeout(TimeValue.timeValueSeconds(2));
		// 将查询条件放入文档查询中
		searchSourceBuilder.query(queryBuilder);
		// 设置高亮
		setHighlight(searchCondition.getKeyword(), searchSourceBuilder);
		// 设置排序方式
		if (sortOrderMap != null && sortOrderMap.size() > 0) {
			setSortType(searchSourceBuilder, sortOrderMap);
		}
		return searchSourceBuilder;
	}

	private void setHighlight(String keyword, SearchSourceBuilder searchSourceBuilder) {
		// 构建高亮查询条件
		HighlightBuilder highlightBuilder = new HighlightBuilder();
		// 设置搞亮查询的前缀、后缀、高亮内容、是否只高亮一次
		highlightBuilder.preTags("<span style='color:orange'>").postTags("</span>")
				.field(keyword).requireFieldMatch(true);
		// 将高亮查询条件放入文档查询中
		searchSourceBuilder.highlighter(highlightBuilder);
	}

	/**
	 * 设置排序方式
	 *
	 * @param searchSourceBuilder
	 * @param sortOrderMap
	 */
	private void setSortType(SearchSourceBuilder searchSourceBuilder, Map<String, SortOrder> sortOrderMap) {
		sortOrderMap.keySet().forEach(key -> searchSourceBuilder.sort(key, sortOrderMap.get(key)));
	}

	/**
	 * 封装结果集
	 *
	 * @param hit
	 * @return
	 */
	private Map<String, Object> getResultMap(SearchHit hit, String key) {
		// 将source内容封装成map集合
		Map<String, Object> sourceMap = hit.getSourceAsMap();
		// 获取高亮碎片化文本
		Text[] text = hit.getHighlightFields().get(key).getFragments();
		// 将高亮后的字段文本构建成字符串
		StringBuffer stringBuffer = new StringBuffer();
		for (Text s : text) {
			stringBuffer.append(s);
		}
		// 将高亮的字段字符串替换sourceMap中没有高亮的字段
		sourceMap.put(key, stringBuffer.toString());
		// 删除某些字段
		sourceMap.remove("@version");
		sourceMap.remove("@timestamp");
		// 转化为驼峰
		return CamelUtil.formatMapWithCamel(sourceMap);
	}

	/**
	 * 搜索内容
	 *
	 * @param searchCondition
	 * @param sortOrderMap
	 * @return
	 * @throws IOException
	 */
	@Override
	public Page searchInfo(SearchCondition searchCondition, Map<String, SortOrder> sortOrderMap) {
		SearchSourceBuilder searchSourceBuilder = buildSearchSource(searchCondition, sortOrderMap);
		// 构建查询请求
		SearchRequest request = new SearchRequest(properties.getIndex());
		// 将查询表达式放入查询请求中
		request.source(searchSourceBuilder);
		// 发送请求获取响应内容
		SearchResponse response = null;
		try {
			response = client.search(request, RequestOptions.DEFAULT);
		} catch (IOException e) {
			e.printStackTrace();
		}
		// 解析封装结果集，构建存放结果集大小为size的List容器
		List<Map<String, Object>> list = new ArrayList<>(searchCondition.getSize());
		for (SearchHit hit : response.getHits().getHits()) {
			// 将sourceMap放入List容器中
			list.add(getResultMap(hit, searchCondition.getKeyword()));
		}
		// 获取总记录数
		long total = response.getHits().getTotalHits();
		Page page = new Page(searchCondition.getCurrent(), searchCondition.getSize());
		page.setRecords(list);
		page.setTotal(total);
		page.setPages(total / searchCondition.getSize() + (total % searchCondition.getSize() == 0 ? 0 : 1));
		return page;
	}

	@Override
	public boolean deleteDocumentById(String id) {
		int statusCode = 0;
		try {
			DeleteRequest request = new DeleteRequest(properties.getIndex(), "_doc", id);
			request.timeout(TimeValue.timeValueSeconds(1));
			statusCode = client.delete(request, RequestOptions.DEFAULT).status().getStatus();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return statusCode == 200;
	}
}
